﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using MSNPSharp;

namespace MessengerBot
{
    /// <summary>
    /// This <see cref="ICommand"/> command handler implements the "help" command.
    /// </summary>
    [Command("help")]
    [Description("shows help for built-in commands")]
    public sealed class HelpCommand : ICommand
    {
        #region ICommand Members

        /// <summary>
        /// Execute the command.
        /// </summary>
        /// <param name="system">
        /// The <see cref="ICommandBasedSystem"/> instance this command belongs to.
        /// </param>
        /// <param name="session">
        /// The <see cref="Session"/> that this command is executed in.
        /// </param>
        /// <param name="arguments">
        /// Any leftover arguments that wasn't parsed into any properties on the command.
        /// </param>
        public void Execute(ICommandBasedSystem system, Session session, string[] arguments)
        {
            string topic = arguments.FirstOrDefault();
            if (topic == null)
                ShowGeneralHelp(system, session);
            else
                ShowSpecificHelp(system, session, topic);
        }

        #endregion

        /// <summary>
        /// Shows a specific help topic.
        /// </summary>
        /// <param name="system">
        /// The <see cref="ICommandBasedSystem"/> instance this command belongs to.
        /// </param>
        /// <param name="session">
        /// The <see cref="Session"/> that this command is executed in.
        /// </param>
        /// <param name="topic">
        /// The topic name to show help for.
        /// </param>
        private void ShowSpecificHelp(ICommandBasedSystem system, Session session, string topic)
        {
            Type commandType = session.Bot.Commands.GetCommandByName(topic);
            if (commandType == null)
                throw new InvalidOperationException("No command with the name '" + topic + "'"); // TODO: Replace with better exception

            session.Conversation.SendTextMessage(
                new TextMessage("command: " + topic)
                {
                    Font = "Courier New",
                });
            foreach (string line in CommandParser.GetHelp(commandType))
                session.Conversation.SendTextMessage(
                    new TextMessage(line)
                    {
                        Font = "Courier New",
                    });
        }

        /// <summary>
        /// Shows general help.
        /// </summary>
        /// <param name="system">
        /// The <see cref="ICommandBasedSystem"/> instance this command belongs to.
        /// </param>
        /// <param name="session">
        /// The <see cref="Session"/> that this command is executed in.
        /// </param>
        private void ShowGeneralHelp(ICommandBasedSystem system, Session session)
        {
            session.Conversation.SendTextMessage(
                new TextMessage("list of commands:")
                {
                    Font = "Courier New",
                });

            var commands = (from commandType in session.Bot.Commands.GetAllCommands()
                            from CommandAttribute commandAttribute in commandType.GetCustomAttributes(typeof(CommandAttribute), true)
                            let descriptionAttribute =
                                commandType.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault() as DescriptionAttribute
                            select new
                            {
                                name = commandAttribute.Name,
                                description = (descriptionAttribute != null) ? descriptionAttribute.Description : string.Empty
                            }
                            into e orderby e.name select e).ToArray();

            int maxCommandLength = commands.Max(e => e.name.Length);

            foreach (var command in commands)
            {
                session.Conversation.SendTextMessage(
                    new TextMessage(" " + (command.name.PadRight(maxCommandLength, ' ') + "  " + command.description).Trim())
                    {
                        Font = "Courier New",
                    });
            }
            session.Conversation.SendTextMessage(
                new TextMessage("To show the help for a specific command, issue the command 'help COMMANDNAME'")
                {
                    Font = "Courier New",
                });
        }
    }
}